home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
archiver
/
pdtar.zip
/
BUFFER.C
next >
Wrap
C/C++ Source or Header
|
1988-05-15
|
13KB
|
611 lines
/*
* Buffer management for public domain tar.
*
* Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
* MS-DOS port 2/87 by Eric Roskos.
* Minix port 3/88 by Eric Roskos.
*
* @(#) buffer.c 1.14 10/28/86 Public Domain - gnu
*/
#include <stdio.h>
#include <errno.h>
#include <sys/types.h> /* For non-Berkeley systems */
#include <fcntl.h>
#include <signal.h>
#include "tar.h"
#include "port.h"
#define STDIN 0 /* Standard input file descriptor */
#define STDOUT 1 /* Standard output file descriptor */
#define PREAD 0 /* Read file descriptor from pipe() */
#define PWRITE 1 /* Write file descriptor from pipe() */
extern char *valloc();
/*
* V7 doesn't have a #define for this.
*/
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#define MAGIC_STAT 105 /* Magic status returned by child, if it
* can't exec compress. We hope compress
* never returns this status! */
/*
* The record pointed to by save_rec should not be overlaid
* when reading in a new tape block. Copy it to record_save_area first, and
* change the pointer in *save_rec to point to record_save_area.
* Saved_recno records the record number at the time of the save.
* This is used by annofile() to print the record number of a file's
* header record.
*/
static union record **save_rec;
static union record record_save_area;
static int saved_recno;
/*
* PID of child compress program, if f_compress.
*/
static int compress_pid;
/*
* Record number of the start of this block of records
*/
static int baserec;
/*
* Error recovery stuff
*/
static int r_error_count;
/*
* Return the location of the next available input or output record.
*/
union record *
findrec()
{
if (ar_record == ar_last)
{
flush_archive();
if (ar_record == ar_last)
return (union record *) NULL; /* EOF */
}
return ar_record;
}
/*
* Indicate that we have used all records up thru the argument.
* (should the arg have an off-by-1? XXX FIXME)
*/
void
userec(rec)
union record *rec;
{
while (rec >= ar_record)
ar_record++;
/*
* Do NOT flush the archive here. If we do, the same argument to
* userec() could mean the next record (if the input block is exactly one
* record long), which is not what is intended.
*/
if (ar_record > ar_last)
abort();
}
/*
* Return a pointer to the end of the current records buffer.
* All the space between findrec() and endofrecs() is available
* for filling with data, or taking data from.
*/
union record *
endofrecs()
{
return ar_last;
}
/*
* Open an archive file. The argument specifies whether we are
* reading or writing.
*
* With DOS, we ALWAYS open the archive in binary mode: whether or not
* to do CRLF translations depends on whether we open the input files
* as binary or ASCII, but we always write the archive without making
* any translations from what this program saw when it did the write.
*/
open_archive(read)
int read;
{
if (ar_file[0] == '-' && ar_file[1] == '\0')
{
if (read)
archive = STDIN;
else
archive = STDOUT;
}
else
if (read)
{
#ifdef MSDOS
archive = 9999; /* for debugging - invalid fd to cause err */
if (!f_phys) /* don't open if we're doing direct drive I/O */
#endif
archive = open(ar_file, O_RDONLY
#ifdef MSDOS
| O_BINARY
#endif
);
}
else
{
#ifdef MSDOS
archive = 9999;
if (!f_phys)
#endif
#ifdef V7
archive = creat(ar_file, 0666);
#else
archive = open(ar_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
0666);
#endif
}
if (archive < 0)
{
perror(ar_file);
exit(EX_BADARCH);
}
/* NOSTRICT */
ar_block = (union record *) valloc((unsigned) blocksize);
if (!ar_block)
{
fprintf(stderr,
"tar: could not allocate memory for blocking factor %d\n",
blocking);
exit(EX_ARGSBAD);
}
ar_record = ar_block;
ar_last = ar_block + blocking;
/*
* Handle compressed archives.
*
* FIXME, currently supported for reading only. FIXME, writing involves
* forking again for a small process that will reblock the output of
* compress to the user's specs.
*/
#ifndef MSDOS
if (f_compress)
{
int pipes[2];
int err;
if (!read)
{
fprintf(stderr,
"tar: cannot write compressed archives yet.\n");
exit(EX_ARGSBAD);
}
/* Create a pipe to get compress's output to us */
err = pipe(pipes);
if (err < 0)
{
perror("tar: cannot create pipe to compress");
exit(EX_SYSTEM);
}
/* Fork compress process */
compress_pid = fork();
if (compress_pid < 0)
{
perror("tar: cannot fork compress");
exit(EX_SYSTEM);
}
/*
* Child process.
*
* Move input to stdin, write side of pipe to stdout, then exec
* compress.
*/
if (compress_pid == 0)
{
(void) close(pipes[PREAD]); /* We won't use it */
if (archive != STDIN)
{
(void) close(STDIN);
err = dup(archive);
if (err != 0)
{
perror(
"tar: cannot dup input to stdin");
exit(EX_SYSTEM);
}
(void) close(archive);
}
if (pipes[PWRITE] != STDOUT)
{
(void) close(STDOUT);
err = dup(pipes[PWRITE]);
if (err != STDOUT)
{
perror(
"tar: cannot dup pipe output");
exit(MAGIC_STAT);
}
(void) close(pipes[PWRITE]);
}
#ifdef V7
execl("/usr/bin/compress", "compress", "-d", (char *)0);
#else
execlp("compress", "compress", "-d", (char *) 0);
#endif
perror("tar: cannot exec compress");
exit(MAGIC_STAT);
}
/*
* Parent process. Clean up. FIXME, note that this may leave
* standard input closed, if the compressed archive was on standard
* input.
*/
(void) close(archive); /* Close compressed archive */
(void) close(pipes[PWRITE]); /* Close write side of pipe */
archive = pipes[PREAD]; /* Read side is our archive */
#ifdef BSD42
f_reblock++; /* Pipe will give random # of bytes */
#endif /* BSD42 */
}
#endif /* MSDOS */
ar_reading = read;
if (read)
{
ar_last = ar_block; /* Set up for 1st block = # 0 */
flush_archive();
}
}
/*
* Remember a union record * as pointing to something that we
* need to keep when reading onward in the file. Only one such
* thing can be remembered at once, and it only works when reading
* an archive.
*/
saverec(pointer)
union record **pointer;
{
save_rec = pointer;
saved_recno = baserec + ar_record - ar_block;
}
/*
* Perform a write to flush the buffer.
*/
fl_write()
{
int err;
int nbytes = blocksize;
rewrite:
#ifdef MSDOS
if (f_phys)
err = physwrite(ar_block->charptr, nbytes);
else
#endif
err = write(archive, ar_block->charptr, nbytes);
if (err == nbytes)
return;
/* multi-volume support on write -- JER */
if (err < 0)
perror(ar_file);
else
#ifdef MSDOS /* DOS version handles volume change in low-level I/O code */
fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
ar_file, blocksize - err);
#else
{
sync(); /* have to flush Minix buffer */
uprintf(ftty,"\ntar: Volume full. Change volumes and press [Enter]: ");
while (ugetc(ftty)!='\n') ;
nbytes -= err;
lseek(archive, 0L, 0);
goto rewrite;
}
#endif
exit(EX_BADARCH);
}
/*
* Handle read errors on the archive.
*
* If the read should be retried, readerror() returns to the caller.
*/
void
readerror()
{
# define READ_ERROR_MAX 10
read_error_flag++; /* Tell callers */
annorec(stderr, tar);
fprintf(stderr, "Read error on ");
perror(ar_file);
if (baserec == 0)
{
/* First block of tape. Probably stupidity error */
exit(EX_BADARCH);
}
/*
* Read error in mid archive. We retry up to READ_ERROR_MAX times and
* then give up on reading the archive. We set read_error_flag for our
* callers, so they can cope if they want.
*/
if (r_error_count++ > READ_ERROR_MAX)
{
annorec(stderr, tar);
fprintf(stde